package com.coderush.admin.core.component;

import com.alibaba.fastjson2.JSONObject;
import jakarta.annotation.Resource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
@ConditionalOnClass({LettuceConnectionFactory.class})
public class RedisClient {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * Redis SET 命令 - 设置指定 key 的值
     *
     * 如果 key 已经存储其他值， SET 就覆写旧值，且无视类型。
     *
     * @param key 键 不能为空
     * @param value 值 不能为空
     * @return true - 成功
     *         false - 失败
     */
    public Boolean setKV(String key, Object value) {

        if (key == null || "".equals(key)
                || value == null || "".equals(value)) {
            return false;
        }

        try {
            redisTemplate.opsForValue().set(key, value);
        }catch (Exception e) {
            throw e;
        }

        return true;
    }

    /**
     *  Redis Setex 命令为指定的 key 设置值及其过期时间。
     *  如果 key 已经存在， SETEX 命令将会替换旧的值。
     *
     * @param key 键
     * @param value 值
     * @param seconds 时长 单位: 秒
     * @return true - 成功
     *             false - 失败
     */
    public Boolean setKVWithTime(String key, Object value, long seconds) {

        if (key == null || "".equals(key)
                || value == null || "".equals(value)
                || seconds <= 0) {
            return false;
        }

        try {
            redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
        }catch (Exception e) {
            throw e;
        }

        return true;
    }

    /**
     * Redis Get 命令用于获取指定 key 的值。如果 key 不存在，返回 null
     * 使用时需要判断返回值是否为 null
     *
     * @param key 键
     * @return Object
     */
    public Object getKV(String key) {

        if (key == null || "".equals(key)) {
            return null;
        }

        try {
            return redisTemplate.opsForValue().get(key);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * Redis Get 命令用于获取指定 key 的值。如果 key 不存在，返回 null
     * 使用时需要判断返回值是否为 null
     *
     * @param key 键
     * @param objectType 转换的类型.class
     * @return T
     * @param <T> 类型.class
     */
    public <T> T getKVForType(String key, Class<T> objectType) {

        if (key == null || "".equals(key)) {
            return null;
        }

        try {
            Object object = redisTemplate.opsForValue().get(key);
            if (object == null) {
                return null;
            }
            return JSONObject.parseObject(JSONObject.toJSONString(object), objectType);
        }catch (Exception e) {
            throw e;
        }
    }

    /**
     * Redis DEL 命令用于删除已存在的键。不存在的 key 会被忽略。
     */
    public Boolean delKV(String key) {
        if (key == null || "".equals(key)) {
            return false;
        }
        try {
            return redisTemplate.delete(key);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * Redis Hset 命令用于为哈希表中的字段赋值 。
     * 如果哈希表不存在，一个新的哈希表被创建并进行 HSET 操作。
     * 如果字段已经存在于哈希表中，旧值将被覆盖。
     *
     * @param key 键
     * @param field 字段
     * @param value 值
     * @return
     *          true - 成功
     *          false - 失败
     */
    public Boolean setHash(String key, Object field, Object value) {

        if (key == null || "".equals(key)
                || field == null || "".equals(field)
                || value == null || "".equals(value)) {
            return false;
        }

        try {
            redisTemplate.opsForHash().put(key, field, value);
        } catch (Exception e) {
            throw e;
        }

        return true;
    }

    /**
     * Redis Hset 命令用于为哈希表中的字段赋值 。并设置过期时长
     * 如果哈希表不存在，一个新的哈希表被创建并进行 HSET 操作。
     * 如果字段已经存在于哈希表中，旧值将被覆盖。
     *
     * @param key 键
     * @param field 字段
     * @param value 值
     * @param seconds 时长 单位: 秒
     * @return
     */
    public Boolean setHashWithTime(String key, Object field, Object value, long seconds) {

        if (key == null || "".equals(key)
                || field == null || "".equals(field)
                || value == null || "".equals(value)
                || seconds <= 0) {
            return false;
        }

        try {
            this.setHash(key, field, value);
            this.expire(key, seconds);
        } catch (Exception e) {
            throw e;
        }

        return true;
    }
    /**
     * Redis Hget 命令用于返回哈希表中指定字段的值。
     *
     * 返回给定字段的值。如果给定的字段或 key 不存在时，返回 null
     *
     * @param key 键
     * @param field 字段
     * @return 返回Object 或 null
     */
    public Object getHash(String key, Object field) {

        if (key == null || "".equals(key)
                || field == null || "".equals(field)) {
            return null;
        }

        try {
            return redisTemplate.opsForHash().get(key, field);
        } catch (Exception e) {
            throw e;
        }

    }

    /**
     * Redis Hget 命令用于返回哈希表中指定字段的值。
     * 返回给定字段的值。如果给定的字段或 key 不存在时，返回 null
     *
     * @param key 键
     * @param field 字段
     * @param objectType 类型.class
     * @return 返回具体类型 或 null
     * @param <T>
     */
    public <T> T getHashForType(String key, Object field, Class<T> objectType) {

        if (key == null || "".equals(key)
                || field == null || "".equals(field)) {
            return null;
        }

        try {

            Object object = this.getHash(key, field);
            if (object == null) {
                return null;
            }

            return JSONObject.parseObject(JSONObject.toJSONString(object), objectType);

        } catch (Exception e) {
            throw e;
        }

    }

    /**
     * Redis Hmset 命令用于同时将多个 field-value (字段-值)对设置到哈希表中。
     * 此命令会覆盖哈希表中已存在的字段。
     * 如果哈希表不存在，会创建一个空哈希表，并执行 HMSET 操作。
     *
     * @param key 值
     * @param map map集合
     * @return true - 成功
     *          false - 失败
     */
    public Boolean setHashMap(String key, Map<Object, Object> map) {

        if (key == null || "".equals(key)
                || map == null || map.size() <= 0) {
            return false;
        }

        try {
            redisTemplate.opsForHash().putAll(key, map);
        } catch (Exception e) {
            throw e;
        }

        return true;
    }

    /**
     *
     * Redis Hmset 命令用于同时将多个 field-value (字段-值)对设置到哈希表中。
     * 此命令会覆盖哈希表中已存在的字段。
     * 如果哈希表不存在，会创建一个空哈希表，并执行 HMSET 操作。
     *
     * @param key 值
     * @param map map集合
     * @param seconds 时长 单位: 秒
     * @return true - 成功
     *          false - 失败
     */
    public Boolean setHashMapWithTime(String key, Map<Object, Object> map, long seconds) {

        if (key == null || "".equals(key)
                || map == null || map.size() <= 0
                || seconds <= 0) {
            return false;
        }

        try {
            this.setHashMap(key, map);
            this.expire(key, seconds);
        } catch (Exception e) {
            throw e;
        }
        return true;
    }

    /**
     * Redis Hgetall 命令用于返回哈希表中，所有的字段和值。
     *
     *  若 key 不存在，返回空列表。
     *
     * @param key 键
     * @return Map集合 或 null
     */
    public Map<Object, Object> getHashMapAll(String key) {

        if (key == null || "".equals(key)) {
            return null;
        }

        try {
            return redisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * Redis Hdel 命令用于删除哈希表 key 中的一个指定字段，不存在的字段将被忽略。
     *
     * @param key 键
     * @param field 字段
     * @return true - 成功
     *          false - 失败
     */
    public Boolean delHashOne(String key, Object field) {

        if (key == null || "".equals(key) || field == null) {
            return false;
        }

        try {
            return redisTemplate.opsForHash().delete(key, field) > 0;
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * Redis Hdel 命令用于删除哈希表 key 中的一个或多个指定字段，不存在的字段将被忽略。
     *
     * @param key 键
     * @param fields 数组
     * @return true - 成功
     *          false - 失败
     */
    public Boolean delHashMore(String key, Object[] fields) {

        if (key == null || "".equals(key)
                || fields == null || fields.length <= 0) {
            return false;
        }

        try {
            return redisTemplate.opsForHash().delete(key, fields) > 0;
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     *
     * (List) 将一个或多个值插入到列表(最左边或者最右边)
     *
     * @param side 取值 left-最左边，right-左右边
     * @param key 键
     * @param value 值-数组类型
     * @return 0L 或 列表长度
     */
    public Long addList(String side, String key, Object[] value) {

        if (side == null || "".equals(side)
                || key == null || "".equals(key)
                || value == null || value.length <= 0) {
            return 0L;
        }

        // 最左边
        if ("left".equals(side)) {
            return redisTemplate.opsForList().leftPushAll(key, value);
        }
        else if ("right".equals(side)) {
            return redisTemplate.opsForList().rightPushAll(key, value);
        }

        return 0L;
    }

    /**
     * 指定缓存失效时间
     *
     * @param key      键
     * @param seconds  时长 单位: 秒
     */
    public Boolean expire(String key, long seconds) {

        if (key == null || "".equals(key)
                || seconds <= 0) {
            return false;
        }

        boolean flag = false;
        try {
            flag = redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
        } catch (Exception e) {
            throw e;
        }

        return flag;
    }
}